﻿using System;
using System.Text;

namespace Red.EngineData.View
{
    /// <summary>
    /// Prepares correct interop expression
    /// </summary>
    public class Interop
    {
        #region Private Variables

        /// <summary>
        /// Characters, in text arguments, which need special support from interop layer
        /// </summary>
        private static char[] _specialCharacters = { '\n', '\t', '\"', '\'', '\\' };

        /// <summary>
        /// Final interop expression in correct format
        /// </summary>
        private string _finalExpression;

        #endregion

        #region Public Methods

        /// <summary>
        /// Creates an function-call expression. MakeCall("func", a, b) is equivalent to Make("func({1},{2})", a, b)
        /// </summary>
        public static Interop MakeCall( string funcName, params object[] callArgs )
        {
            string format = funcName + "(";
            for ( int i = 0; i < callArgs.Length; ++i )
            {
                if ( i > 0 ) format += ",";
                format += "{" + i.ToString() + "}";
            }
            format += ")";
            return new Interop( format, callArgs );
        }

        /// <summary>
        /// Creates an expression. Generic version for advanced use, use only when you know what your are doing.
        /// </summary>
        public static Interop Make( string format, params object[] args )
        {
            return new Interop( format, args );
        }

        /// <summary>
        /// Returns final interop expression in correct format
        /// </summary>
        /// <returns>Correct interop expression</returns>
        public override string ToString()
        {
            return _finalExpression;
        }

        /// <summary>
        /// Converts Interop object to string
        /// </summary>
        /// <param name="obj">Interop object</param>
        /// <returns>Completed interop expression</returns>
        public static implicit operator string( Interop obj )
        {
            return obj.ToString();
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Converts user expression to correct interop expression and packs result to wrapper
        /// </summary>
        /// <param name="format">Interop expression format</param>
        /// <param name="args">Interop expression arguments</param>
        private Interop( string format, params object[] args )
        {
            _finalExpression = Convert( format, args );
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Checks all arguments and and 
        /// </summary>
        /// <param name="format">Interop expression format</param>
        /// <param name="args">Interop expression arguments</param>
        /// <returns></returns>
        private string Convert( string format, params object[] args )
        {
            for ( int i = 0; i < args.Length; ++i )
            {
                if ( args[i] != null )
                    args[i] = ConvertStringForInterop( args[i].ToString() );
            }

            return string.Format( format, args );
        }

        /// <summary>
        /// Converts argument to correct interop text format
        /// </summary>
        /// <param name="text">Raw interop expression argument</param>
        /// <returns>Final interop expression argument in correct format</returns>
        private string ConvertStringForInterop( string text )
        {
            StringBuilder newExp = new StringBuilder();
            newExp.Append( "\"" );

            if ( HasSpecialCharacters( text ) == false && ContainsUnicodeCharacter( text ) == false )
            {
                newExp.Append( text );
            }
            else
            {
                foreach ( char cur in text )
                {
                    if ( Array.IndexOf( _specialCharacters, cur ) != -1 )
                    {
                        newExp.Append( "\\" );
                    }

                    if ( cur > 127 )
                    {
                        // This character is too big for ASCII
                        string encodedValue = "&#" + ( (int)cur ).ToString( "x4" );
                        newExp.Append( encodedValue );
                        continue;
                    }

                    newExp.Append( cur );
                }
            }

            newExp.Append( "\"" );

            return newExp.ToString();
        }

        /// <summary>
        /// Checks whether the text contains special characters
        /// </summary>
        /// <param name="text">Text to analyze</param>
        private bool HasSpecialCharacters( string text )
        {
            return text.IndexOfAny( _specialCharacters ) != -1;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public bool ContainsUnicodeCharacter( string input )
        {
            for ( int i = 0; i < input.Length; ++i )
            {
                if ( input[i] > char.MaxValue )
                    return true;
            }
            return false;
        }

        #endregion
    }
}
